<template>
  <el-card class="setbuild">
    <template #header>
      <div class="card-header">
        <span style="font-weight: 600; font-size: 18px;cursor: pointer;" @click="()=>{router.push('/')}">查看房户信息</span>
        <span style="font-weight: 600; font-size: 18px;cursor: pointer;" @click="()=>{router.push('/setBuild')}">/分层分户</span>
        <span style="font-weight: 600; font-size: 18px;cursor: pointer;" @click="()=>{router.push('/setInfo')}">/录入信息</span>
        <span class="myIcon" style="float: right" @click="reset">
          <Refresh /> 
        </span>
      </div>
    </template>
    <div>
      <el-steps :active="data.active" align-center finish-status="success">
        <el-step title="区域绘制" />
        <el-step title="户型切分" />
        <el-step title="楼层分层" />
      </el-steps>
      <div style="margin: 30px 10px">
        <div>
          <span>绘制户型：</span>
          <span class="myIcon" v-show="data.active == 0" @click="drawPolygon">
            <FullScreen />
            绘制图形</span
          >
          <span class="myIcon" v-show="data.active == 1" @click="drawLine">
            <scissor />
            裁切图形</span
          >
          <span class="myIcon" v-show="data.active == 2" @click="drawPoint">
            <histogram />
            楼层分层</span
          >
          <span class="buildnum" v-show="data.active == 2" >
           楼层数 <el-input v-model="data.floorNumber" type="number"></el-input></span
          >
        </div>
        <!-- 表头 -->
        <div class="textInput">
          <el-input v-model="inputArr[0]"></el-input>
          <el-input v-model="inputArr[1]"></el-input>
          <el-input style="width: 40%" v-model="inputArr[2]"></el-input>
          <el-input style="width: 40%" v-model="inputArr[3]" v-show="data.active == 1"></el-input>
          
        </div>
        <!-- listContent -->
        <div class="textInput" v-for="(item, index) of data.unitArr" :key="index">
          <el-input v-model="item[0]" disabled></el-input>
          <el-input v-model="data.buildName"></el-input>
          <el-input style="width: 40%" v-model="item[1]"></el-input>
          <img src="@/assets/img/position.png" alt="定位" v-show="data.active == 1" @click="toFlash(item[2])">
        </div>
        <!-- 楼层高度 -->
         <div class="pointList" v-show="data.active == 2">
          最低点：<el-input v-model="data.heightArr[0]"></el-input>
          分割点：<el-input v-model="data.heightArr[1]"></el-input>
          最高点：<el-input v-model="data.heightArr[2]"></el-input>
         </div>
        <!-- 按钮 -->
        <el-button
          type="info"
          v-if="data.active < 2"
          style="margin: 12px; float: right"
          @click="next"
          >下一步</el-button
        >
        <el-button type="success" v-else style="margin: 12px; float: right" @click="toLayer"
          >楼层分层</el-button
        >
        <el-button type="primary"  v-if="data.active == 3" style="margin: 12px; float: right" @click="toAddHouse"
          >生成数据</el-button
        >
      </div>
    </div>
  </el-card>
</template>
<script setup>
import { onMounted, getCurrentInstance, reactive,onUnmounted } from 'vue'
import { endDraw, toDraw } from '@/tool/draw.js'
import * as Cesium from 'cesium'
import * as turf from '@turf/turf'
import { addHouse } from '@/api/houseApi'
import { FullScreen, Scissor, Histogram, Refresh } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import polygonCut from '@/tool/polygonCut'
import { useRouter } from 'vue-router'
const router = useRouter()
// 全局变量
const { appContext } = getCurrentInstance()
const global = appContext.config.globalProperties
// 响应数据
const data = reactive({
  active: 0,
  unitArr: [],
  buildName: 'xxx小区xx',
  heightArr:[],
  floorNumber:0
})
let polygonEntity,polygonGsojson
// 存储地图对象
let viewer
//这个数据不需要变，所以不需要设置成响应式数据
const inputArr = ['分户坐标', '地址前缀', '单位','定位']

// 下一步
const next = () => {
  // 只有绘制完成后才能进行下一步
  if (!data.unitArr.length && !polygonEntity) {
    ElMessage.info('请先绘制！')
    return
  }
  data.active++
}

// 绘制多边形
const drawPolygon = () => {
  ElMessage.info('请绘制图形，右键结束绘制')
  // 将原来数据清空，准备重新绘制
  viewer.entities.removeAll()
  data.unitArr = []
  polygonEntity = null
  // 调用画笔，绘制结束后转换数据
  toDraw(viewer, 'polygon', (res) => {
    // 将绘制出来的实体保存一份，在裁剪时用于恢复视图。（老师说的伏笔。哈哈哈）
    polygonEntity = res
    // 1、可视化窗口中显示的是经纬度坐标
    // 2、数据库中存储的是 GeoJSON数据格式
    // 3、多边形切割时也需要 geojson数据
    // 所以需要将 笛卡尔坐标转换为经纬度
    let car3_ps = res.polygon.hierarchy._value.positions
    let arr = []
    for (let i = 0; i < car3_ps.length; i++) {
      let _cartographic = Cesium.Cartographic.fromCartesian(car3_ps[i])
      let _lat = Cesium.Math.toDegrees(_cartographic.latitude)
      let _lng = Cesium.Math.toDegrees(_cartographic.longitude)
      arr.push([_lng, _lat])
    }
    // 首位相连
    arr.push(arr[0])
    // 利用turf方法生成geojson数据
     polygonGsojson = turf.polygon([arr])
     // 将所需的坐标数据添加到data 中，方便渲染到视图中
    data.unitArr.push([polygonGsojson.geometry.coordinates.toString(), 1, res])
  })
}
// 分割多边形
let polygonCollection
const  drawLine =()=>{
  ElMessage.info('请绘制图形，右键结束绘制')
  // 进行重复裁切时，将实体恢复至原始模样
  viewer.entities.removeAll()
  viewer.entities.add(polygonEntity)

  toDraw(viewer,'line',(res)=>{
    // 转换坐标，生成geojson数据
    let car3_ps = res.polyline.positions._value
    let arr = []
    for (let i = 0; i < car3_ps.length; i++) {
      let _cartographic = Cesium.Cartographic.fromCartesian(car3_ps[i])
      let _lat = Cesium.Math.toDegrees(_cartographic.latitude)
      let _lng = Cesium.Math.toDegrees(_cartographic.longitude)
      arr.push([_lng, _lat])
    }
    // 利用turf方法生成geojson数据
    const lineGeojson = turf.lineString(arr);
    // 使用多边形裁切方法，line的绘制不合规范时(只进不出、根本不进入），会抛出错误

    try {
      polygonCollection =  polygonCut(polygonGsojson,lineGeojson)
      if(polygonCollection.features.length ==1){
        ElMessage.error('请切割区域')
        return
      }
    } catch (error) {
      ElMessage.error(error)
      return
    }
    // 将裁切后的多边形加载成实体，原来的清除
    Cesium.GeoJsonDataSource.load(polygonCollection,{clampToGround:true}).then(info =>{
      viewer.entities.removeAll()
      data.unitArr = []
      info.entities.values.forEach((item,index) =>{
        item.polygon.material = Cesium.Color.fromRandom({alpha:0.5})
        viewer.entities.add(item)
        data.unitArr.push([
          polygonCollection.features[index].geometry.coordinates.toString(),
          data.unitArr.length +1,
          item // 添加实体，以便于后续高亮时传参
        ])
      })
    })

  })

}
// 定位闪烁
let initColor
const toFlash = (entity)=>{
  // 闪烁1.5s后停止闪烁
  // 使用CallbackProperty方法
  // bug ：在连续点击时，会变成白膜并报错说找不到 color
  if(!initColor){
   initColor  = entity.polygon.material.color._value
  }
  //  initColor  = entity.polygon.material // 这样是不行的，initColor变成了callback函数，会一直执行下去

  console.log(initColor,'颜色的格式');
  // 当在闪烁期间再次点击，entity.polygon.material得到的是一个callback函数，自然是没有 color._value属性的，所以赋值为 undefined，当最近的一个计时器执行
  // entity.polygon.material 被赋值为 undefined，那再次点击时，对initColor赋值时寻找属性 .color._value会报错
  let x = 1
  let flog = true
  entity.polygon.material = new Cesium.ColorMaterialProperty(new Cesium.CallbackProperty(()=>{
    if(flog){
      x = x-0.02
      if(x<0){
        flog = false
      }
    }else {
      x = x+0.02
      if(x>=1){
        flog = true
      }
    }
    return Cesium.Color.SKYBLUE.withAlpha(x)
  },false))
  setTimeout(() => {
    if(!initColor){
      return
    }
    entity.polygon.material = initColor
    initColor = null
    // 不清空的话，之后的闪烁的结束都会变成最开始的那个面的颜色，
    // 但是在这清空的话，因为每点击一次都会开启一次定时器，当initColor被设为null后，在此之后的定时器执行赋值时就会报错
    // 加个判断刚好解决
  }, 1500);
}
// 绘制点
const drawPoint = ()=>{
  ElMessage.info('请绘制底层，二楼以及顶楼高度点')
  // 清空上一次操作留下的点数据
  data.heightArr = []
  // 从0开始删除：有bug。这是因为，当viewer.entities.values这个数组发生变化时，会重新排序（仅限于这个数组），所以会漏掉最开始的奇数位的点数据
  // viewer.entities.values.forEach(item =>{
  //   item.point && viewer.entities.remove(item)
  // })
  // 解决方法：从末尾开始删除
  for(let i = viewer.entities.values.length -1 ;i>=0;i--){
    let item = viewer.entities.values[i]
    item.point && viewer.entities.remove(item)
  }
  toDraw(viewer,'point',(res)=>{
    // 拿到高度
    const height = Cesium.Cartographic.fromCartesian(res.position._value).height
    data.heightArr.push(height)
    if(data.heightArr.length == 3){
      data.heightArr.sort((a,b)=>a-b)
      endDraw()
    }
  })
}
// 最后一步楼层分层
let heightList = []
const toLayer = () => {
  // 进行拆分前先检验，楼层数是否正确
  if(!data.floorNumber){
    ElMessage.info('请输入楼层数')
    return
  }
  // 清空绘制的点和未分层的面
  viewer.entities.removeAll()
  // 清除拆分后的楼层(如果有的话），即primitive，但是3DTiles 也是添加的 primitive，所以不能直接 removeAll
  heightList.length && heightList.forEach(item =>{
    viewer.scene.primitives.remove(item)
 })
 heightList = []
  
// 因为进行了切分，所以要对裁切后的每一个实体进行遍历添加primitive
 // 计算出每层楼的高度，然后通过循环嵌套，添加每一层的primitive
 let itemHeight = (data.heightArr[2] - data.heightArr[1]) / (data.floorNumber - 1)
 for (let j = 0; j < data.unitArr.length; j++) {
   for(let i = 0;i<data.floorNumber;i++){
     let height,extrudedHeight
    // 第一层的高度有点不一样，所以需要单独拎出来
    if(i == 0){
      height = data.heightArr[0],
      extrudedHeight = data.heightArr[1]
    }else{
      // 这有个bug，当用户通过input框对三个点的高度进行修改后，高度值变成了 string格式，做减法或者直接赋值时会存在隐式转换，不会出问题，但是在这的加法中，会变成字符串拼接
      height = Number(data.heightArr[1]) + (i-1)*itemHeight
      extrudedHeight = Number(data.heightArr[1]) + i*itemHeight
    }
    // 新建紧贴3DTiles的primitive
     let primitive = new Cesium.ClassificationPrimitive({
       geometryInstances:new Cesium.GeometryInstance({
         geometry:new Cesium.PolygonGeometry({
           polygonHierarchy:new Cesium.PolygonHierarchy(data.unitArr[j][2].polygon.hierarchy._value.positions),
           height,
           extrudedHeight,
         }),
         attributes:{
           color:Cesium.ColorGeometryInstanceAttribute.fromColor(
             Cesium.Color.fromRandom({alpha:0.5})
           )
         }
       }),
       classificationType:Cesium.ClassificationType.CESIUM_3D_TILE
     })
     viewer.scene.primitives.add(primitive)
     heightList.push(primitive)
   }
 }
 // 当成功分层后才表示这一步成功完成
 if(heightList.length){
   data.active = 3
 }
}
// reset
const reset = ()=>{
  endDraw()
  viewer.entities.removeAll()
  heightList.length && heightList.forEach(item =>{
    viewer.scene.primitives.remove(item)
 })
 heightList = []
 data.active = []
 data.unitArr = []
 data.buildName = 'xxx小区xx'
 data.heightArr = []
 data.floorNumber = 0
 polygonEntity = null
 polygonGsojson = null
  polygonCollection =null
}
// 生成并提交数据
const toAddHouse =async()=>{
  let polygonJson = JSON.stringify(polygonGsojson.geometry)
  let polygonJsonArr = polygonCollection?polygonCollection.features.map((item)=>{
    return JSON.stringify(item.geometry)
  }):[]
  let unitArr = data.unitArr.map((item) =>{
    return Number(item[1])
  })
  let heightArr = data.heightArr.map((item) =>{
    return Number(item)
  })
  const res = await addHouse({
    polygonJson,
    polygonJsonArr,
    unitArr,
    heightArr,
    name:data.buildName,
    floorNum:Number(data.floorNumber)
  })
  if(res.code == 200){
    ElMessage.success('提交成功')
    reset()
  }

}

onMounted(() => {
  // 获取地图变量
  viewer = global.$viewer
})
// 销毁组件时，同时清除地图影响
onUnmounted(()=>{
  reset()
})
</script>
<style lang="scss">
.setbuild {
  width: 30%;
  position: absolute;
  top: 4%;
  left: 4%;
  z-index: 999;

  .buildnum {
    float: right;
    width: 34%;

    .el-input {
      width: 50%;
      height: 24px
    }
  }

  .myIcon {
    cursor: pointer;

    svg {
      width: 20px;
      position: relative;
      top: 5px;
      margin-right: 3px;
    }
  }

  .textInput {
    display: flex;
    width: 100%;

    .el-input {
      margin: 5px 1%;

      .el-input__inner {
        text-align: center;
      }
    }

    img {
      margin: 10px 4.6%;
      width: 14%;
      height: 25px;
      cursor: pointer;
    }
  }

  .pointList {
    display: flex;
    font-size: 14px;
    margin: 20px 4px;
    line-height: 30px;

    .el-input {
      width: 15%;
      margin-right: 4%;
      margin-top: 4px;
      height: 24px
    }
  }
}
</style>
